package com.kexilo.exception.autoconfigure;

import com.kexilo.exception.properties.ExceptionProperties;
import com.kexilo.exception.service.ExceptionService;
import com.kexilo.exception.service.ExceptionServiceImpl;
import com.kexilo.exception.monitor.ExceptionMonitor;
import com.kexilo.exception.deadletter.DeadLetterQueue;
import com.kexilo.exception.handler.GlobalExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import javax.sql.DataSource;
import java.time.LocalDateTime;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 异常处理插件自动配置类
 * 根据条件自动装配异常处理相关组件
 * 
 * @author Kexilo
 */
@AutoConfiguration
@ConditionalOnClass({ExceptionService.class})
@ConditionalOnProperty(value = "kexilo.plugin.exception.enabled", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(ExceptionProperties.class)
@EnableAsync
public class ExceptionAutoConfiguration {
    
    private static final Logger log = LoggerFactory.getLogger(ExceptionAutoConfiguration.class);
    
    /**
     * 配置JdbcTemplate Bean
     * 复用admin模块的DataSource配置
     */
    @Bean
    @ConditionalOnClass({DataSource.class, JdbcTemplate.class})
    @ConditionalOnMissingBean(JdbcTemplate.class)
    @ConditionalOnProperty(value = "kexilo.plugin.exception.storage-strategy", havingValue = "DATABASE", matchIfMissing = true)
    public JdbcTemplate exceptionJdbcTemplate(DataSource dataSource) {
        log.info("配置异常处理插件JdbcTemplate，复用admin模块DataSource");
        return new JdbcTemplate(dataSource);
    }
    
    /**
     * 配置ExceptionService Bean
     */
    @Bean
    @ConditionalOnMissingBean(ExceptionService.class)
    public ExceptionService exceptionService(ExceptionProperties properties,
                                           @Autowired(required = false) JdbcTemplate jdbcTemplate) {
        
        log.info("配置异常处理服务，存储策略: {}, 通知策略: {}", 
                properties.getStorageStrategy(), properties.getNotificationStrategy());
        return new ExceptionServiceImpl(properties, jdbcTemplate);
    }
    
    /**
     * 配置异步任务执行器（专用于异常处理）
     */
    @Bean("exceptionTaskExecutor")
    @ConditionalOnProperty(value = "kexilo.plugin.exception.processing-mode", havingValue = "ASYNC", matchIfMissing = true)
    @ConditionalOnMissingBean(name = "exceptionTaskExecutor")
    public ThreadPoolTaskExecutor exceptionTaskExecutor(ExceptionProperties properties) {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(8);
        executor.setQueueCapacity(500);
        executor.setThreadNamePrefix("kexilo-exception-");
        executor.setKeepAliveSeconds(60);
        executor.setAllowCoreThreadTimeOut(false);
        
        // 拒绝策略：调用者运行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        
        // 等待任务完成后关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        
        executor.initialize();
        
        log.info("配置异常处理异步执行器，核心线程数: 2, 最大线程数: 8");
        return executor;
    }
    
    /**
     * 初始化异常表结构
     */
    @Bean
    @ConditionalOnProperty(value = "kexilo.plugin.exception.database.auto-create-table", havingValue = "true")
    public ApplicationRunner exceptionTableInitializer(ExceptionService exceptionService, 
                                                      ExceptionProperties properties,
                                                      @Autowired(required = false) JdbcTemplate jdbcTemplate) {
        return args -> {
            if (properties.getStorageStrategy() == ExceptionProperties.StorageStrategy.DATABASE && 
                jdbcTemplate != null) {
                log.info("开始初始化异常表结构...");
                try {
                    createExceptionTableIfNotExists(jdbcTemplate, properties);
                    log.info("异常表结构初始化完成");
                } catch (Exception e) {
                    log.warn("异常表结构初始化失败", e);
                }
            }
        };
    }
    
    /**
     * 定时清理过期异常记录
     */
    @Bean
    @ConditionalOnProperty(value = "kexilo.plugin.exception.cleanup.enabled", havingValue = "true", matchIfMissing = true)
    public ExceptionCleanupScheduler exceptionCleanupScheduler(ExceptionService exceptionService, 
                                                              ExceptionProperties properties) {
        log.info("配置异常记录定时清理，保留天数: {}", properties.getCleanup().getDataRetentionDays());
        return new ExceptionCleanupScheduler(exceptionService, properties);
    }
    
    /**
     * 异常监控器
     */
    @Bean
    @ConditionalOnMissingBean(ExceptionMonitor.class)
    @ConditionalOnProperty(value = "kexilo.plugin.exception.monitor.enabled", havingValue = "true", matchIfMissing = true)
    public ExceptionMonitor exceptionMonitor() {
        log.info("配置异常监控器（原core-common功能）");
        return new ExceptionMonitor();
    }
    
    /**
     * 死信队列
     */
    @Bean
    @ConditionalOnMissingBean(DeadLetterQueue.class)
    @ConditionalOnProperty(value = "kexilo.plugin.exception.deadletter.enabled", havingValue = "true", matchIfMissing = true)
    public DeadLetterQueue deadLetterQueue() {
        log.info("配置死信队列（原core-common功能）");
        return new DeadLetterQueue();
    }
    
    /**
     * 异常监控服务
     */
    @Bean
    @ConditionalOnProperty(value = "kexilo.plugin.exception.monitor.enabled", havingValue = "true", matchIfMissing = true)
    public ExceptionMonitorService exceptionMonitorService(ExceptionService exceptionService, 
                                                          ExceptionProperties properties) {
        log.info("配置异常处理监控服务");
        return new ExceptionMonitorService(exceptionService, properties);
    }
    
    /**
     * 异常处理健康检查
     */
    @Bean
    @ConditionalOnClass(name = "org.springframework.boot.actuate.health.HealthIndicator")
    @ConditionalOnProperty(value = "kexilo.plugin.exception.health.enabled", havingValue = "true")
    public ExceptionHealthIndicator exceptionHealthIndicator(ExceptionService exceptionService, 
                                                           ExceptionProperties properties) {
        log.info("配置异常处理健康检查");
        return new ExceptionHealthIndicator(exceptionService, properties);
    }
    
    /**
     * 全局异常处理器
     */
    @Bean
    @ConditionalOnMissingBean(GlobalExceptionHandler.class)
    @ConditionalOnProperty(value = "kexilo.plugin.exception.global-handler.enabled", havingValue = "true", matchIfMissing = true)
    public GlobalExceptionHandler globalExceptionHandler() {
        log.info("配置全局异常处理器（原core-common的GlobalExceptionHandler）");
        return new GlobalExceptionHandler();
    }
    
    /**
     * 异常处理管理端点（集成Spring Boot Actuator）
     */
    @Bean
    @ConditionalOnClass(name = "org.springframework.boot.actuate.endpoint.annotation.Endpoint")
    @ConditionalOnProperty(value = "kexilo.plugin.exception.management-endpoint.enabled", havingValue = "true")
    public ExceptionManagementEndpoint exceptionManagementEndpoint(ExceptionService exceptionService) {
        log.info("配置异常处理管理端点");
        return new ExceptionManagementEndpoint(exceptionService);
    }
    
    /**
     * 创建异常表
     */
    private void createExceptionTableIfNotExists(JdbcTemplate jdbcTemplate, ExceptionProperties properties) {
        String tableName = properties.getDatabase().getExceptionTable();
        
        // 检查表是否存在
        try {
            String checkSql = "SELECT COUNT(*) FROM information_schema.tables " +
                             "WHERE table_schema = DATABASE() AND table_name = ?";
            Integer count = jdbcTemplate.queryForObject(checkSql, Integer.class, tableName);
            
            if (count != null && count > 0) {
                log.info("异常表 {} 已存在", tableName);
                return;
            }
        } catch (Exception e) {
            log.debug("检查表存在性失败，继续创建表", e);
        }
        
        // 创建表
        String createTableSql = "CREATE TABLE IF NOT EXISTS " + tableName + " (\n" +
                "  id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',\n" +
                "  exception_id VARCHAR(64) NOT NULL COMMENT '异常ID',\n" +
                "  trace_id VARCHAR(64) COMMENT '链路追踪ID',\n" +
                "  exception_type VARCHAR(50) COMMENT '异常类型',\n" +
                "  exception_class VARCHAR(255) COMMENT '异常类名',\n" +
                "  exception_message TEXT COMMENT '异常消息',\n" +
                "  stack_trace LONGTEXT COMMENT '异常堆栈',\n" +
                "  occur_time DATETIME COMMENT '异常发生时间',\n" +
                "  user_id VARCHAR(64) COMMENT '用户ID',\n" +
                "  user_name VARCHAR(100) COMMENT '用户名称',\n" +
                "  request_url VARCHAR(500) COMMENT '请求URL',\n" +
                "  request_method VARCHAR(10) COMMENT '请求方法',\n" +
                "  request_params LONGTEXT COMMENT '请求参数',\n" +
                "  user_ip VARCHAR(50) COMMENT '用户IP',\n" +
                "  user_agent VARCHAR(1000) COMMENT '用户代理',\n" +
                "  browser_type VARCHAR(50) COMMENT '浏览器类型',\n" +
                "  operating_system VARCHAR(50) COMMENT '操作系统',\n" +
                "  level VARCHAR(20) DEFAULT 'ERROR' COMMENT '异常级别',\n" +
                "  status VARCHAR(20) DEFAULT 'PENDING' COMMENT '处理状态',\n" +
                "  source VARCHAR(50) COMMENT '异常来源',\n" +
                "  business_module VARCHAR(100) COMMENT '业务模块',\n" +
                "  tags VARCHAR(200) COMMENT '异常标签',\n" +
                "  extra_info LONGTEXT COMMENT '额外信息',\n" +
                "  remark VARCHAR(500) COMMENT '处理备注',\n" +
                "  create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n" +
                "  update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n" +
                "  INDEX idx_exception_id (exception_id),\n" +
                "  INDEX idx_trace_id (trace_id),\n" +
                "  INDEX idx_exception_type (exception_type),\n" +
                "  INDEX idx_exception_class (exception_class),\n" +
                "  INDEX idx_occur_time (occur_time),\n" +
                "  INDEX idx_status (status),\n" +
                "  INDEX idx_create_time (create_time)\n" +
                ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='系统异常记录表'";
        
        try {
            jdbcTemplate.execute(createTableSql);
            log.info("异常表 {} 创建成功", tableName);
        } catch (Exception e) {
            log.error("创建异常表失败: {}", tableName, e);
        }
    }
    
    /**
     * 异常清理定时调度器
     */
    public static class ExceptionCleanupScheduler {
        
        private final ExceptionService exceptionService;
        private final ExceptionProperties properties;
        private final Logger log = LoggerFactory.getLogger(ExceptionCleanupScheduler.class);
        
        public ExceptionCleanupScheduler(ExceptionService exceptionService, ExceptionProperties properties) {
            this.exceptionService = exceptionService;
            this.properties = properties;
        }
        
        @Scheduled(fixedDelayString = "#{@exceptionProperties.cleanup.cleanupInterval.toMillis()}")
        public void cleanupExpiredExceptions() {
            if (!properties.getCleanup().isEnabled()) {
                return;
            }
            
            try {
                log.info("开始清理过期异常记录");
                int deleted = exceptionService.cleanupExpiredExceptions(properties.getCleanup().getDataRetentionDays());
                log.info("清理过期异常记录完成，删除数量: {}", deleted);
            } catch (Exception e) {
                log.error("清理过期异常记录失败", e);
            }
        }
    }
    
    /**
     * 异常监控服务
     */
    public static class ExceptionMonitorService {
        
        private final ExceptionService exceptionService;
        private final ExceptionProperties properties;
        private final Logger log = LoggerFactory.getLogger(ExceptionMonitorService.class);
        
        public ExceptionMonitorService(ExceptionService exceptionService, ExceptionProperties properties) {
            this.exceptionService = exceptionService;
            this.properties = properties;
        }
        
        @Scheduled(fixedDelayString = "#{@exceptionProperties.monitor.monitorInterval.toMillis()}")
        public void monitorExceptionFrequency() {
            if (!properties.getMonitor().isEnabled()) {
                return;
            }
            
            try {
                var stats = exceptionService.getExceptionStatistics();
                Integer todayCount = (Integer) stats.get("todayCount");
                
                if (todayCount != null && todayCount > properties.getMonitor().getFrequencyAlertThreshold()) {
                    log.warn("异常频率告警: 今日异常数量 {} 超过阈值 {}", 
                            todayCount, properties.getMonitor().getFrequencyAlertThreshold());
                }
                
                log.debug("异常监控统计: {}", stats);
                
            } catch (Exception e) {
                log.error("异常频率监控失败", e);
            }
        }
        
        public java.util.Map<String, Object> getMonitorReport() {
            java.util.Map<String, Object> report = new java.util.HashMap<>();
            
            try {
                report.put("serviceAvailable", exceptionService.isAvailable());
                report.put("healthStatus", exceptionService.getHealthStatus());
                report.put("statistics", exceptionService.getExceptionStatistics());
                report.put("trends", exceptionService.getExceptionTrends(7));
                report.put("rankings", exceptionService.getExceptionRankings("type", 10));
                report.put("monitorConfig", java.util.Map.of(
                    "enabled", properties.getMonitor().isEnabled(),
                    "monitorInterval", properties.getMonitor().getMonitorInterval(),
                    "frequencyAlertThreshold", properties.getMonitor().getFrequencyAlertThreshold()
                ));
                
            } catch (Exception e) {
                log.error("获取监控报告失败", e);
                report.put("error", e.getMessage());
            }
            
            return report;
        }
    }
    
    /**
     * 异常处理健康检查指示器
     */
    public static class ExceptionHealthIndicator {
        
        private final ExceptionService exceptionService;
        private final ExceptionProperties properties;
        
        public ExceptionHealthIndicator(ExceptionService exceptionService, ExceptionProperties properties) {
            this.exceptionService = exceptionService;
            this.properties = properties;
        }
        
        public String checkHealth() {
            try {
                if (!properties.isEnabled()) {
                    return "DISABLED";
                }
                
                boolean available = exceptionService.isAvailable();
                return available ? "UP" : "DOWN";
                
            } catch (Exception e) {
                return "DOWN: " + e.getMessage();
            }
        }
        
        public String getServiceInfo() {
            return String.format("ExceptionService[%s] - StorageStrategy: %s, NotificationStrategy: %s, ProcessingMode: %s", 
                    exceptionService.getServiceName(),
                    properties.getStorageStrategy(),
                    properties.getNotificationStrategy(),
                    properties.getProcessingMode());
        }
        
        public java.util.Map<String, Object> getDetailedHealth() {
            java.util.Map<String, Object> details = new java.util.HashMap<>();
            
            details.put("enabled", properties.isEnabled());
            details.put("storageStrategy", properties.getStorageStrategy());
            details.put("notificationStrategy", properties.getNotificationStrategy());
            details.put("processingMode", properties.getProcessingMode());
            details.put("globalHandlerEnabled", properties.isGlobalHandlerEnabled());
            details.put("deadLetterQueueEnabled", properties.isDeadLetterQueueEnabled());
            details.put("deduplicationEnabled", properties.isDeduplicationEnabled());
            details.put("available", exceptionService.isAvailable());
            details.put("healthStatus", exceptionService.getHealthStatus());
            
            return details;
        }
    }
    
    /**
     * Kexilo全局异常处理器
     */
    public static class KexiloGlobalExceptionHandler {
        
        private final ExceptionService exceptionService;
        private final ExceptionProperties properties;
        private final Logger log = LoggerFactory.getLogger(KexiloGlobalExceptionHandler.class);
        
        public KexiloGlobalExceptionHandler(ExceptionService exceptionService, ExceptionProperties properties) {
            this.exceptionService = exceptionService;
            this.properties = properties;
        }
        
        public void handleException(Exception exception, jakarta.servlet.http.HttpServletRequest request) {
            try {
                // 构建异常记录
                com.kexilo.exception.domain.ExceptionRecord record = 
                    com.kexilo.exception.domain.ExceptionRecord.builder()
                        .exceptionType(determineExceptionType(exception))
                        .exceptionClass(exception.getClass().getSimpleName())
                        .exceptionMessage(exception.getMessage())
                        .stackTrace(getStackTrace(exception))
                        .occurTime(LocalDateTime.now())
                        .requestUrl(request != null ? request.getRequestURL().toString() : null)
                        .requestMethod(request != null ? request.getMethod() : null)
                        .userIp(request != null ? getClientIpAddress(request) : null)
                        .userAgent(request != null ? request.getHeader("User-Agent") : null)
                        .source("WEB")
                        .level("ERROR")
                        .status("PENDING")
                        .build();
                
                // 记录异常
                if (properties.getProcessingMode() == ExceptionProperties.ProcessingMode.ASYNC) {
                    exceptionService.recordExceptionAsync(record);
                } else {
                    exceptionService.recordException(record);
                }
                
            } catch (Exception e) {
                log.error("全局异常处理失败", e);
            }
        }
        
        private String determineExceptionType(Exception exception) {
            String className = exception.getClass().getSimpleName();
            if (className.contains("Business") || className.contains("Validation")) {
                return "BUSINESS";
            } else if (className.contains("SQL") || className.contains("Database")) {
                return "DATABASE";
            } else if (className.contains("Network") || className.contains("Timeout")) {
                return "NETWORK";
            } else {
                return "SYSTEM";
            }
        }
        
        private String getStackTrace(Exception exception) {
            java.io.StringWriter sw = new java.io.StringWriter();
            java.io.PrintWriter pw = new java.io.PrintWriter(sw);
            exception.printStackTrace(pw);
            return sw.toString();
        }
        
        private String getClientIpAddress(jakarta.servlet.http.HttpServletRequest request) {
            String xForwardedFor = request.getHeader("X-Forwarded-For");
            if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
                return xForwardedFor.split(",")[0].trim();
            }
            
            String xRealIp = request.getHeader("X-Real-IP");
            if (xRealIp != null && !xRealIp.isEmpty()) {
                return xRealIp;
            }
            
            return request.getRemoteAddr();
        }
    }
    
    /**
     * 异常管理端点
     */
    public static class ExceptionManagementEndpoint {
        
        private final ExceptionService exceptionService;
        private final Logger log = LoggerFactory.getLogger(ExceptionManagementEndpoint.class);
        
        public ExceptionManagementEndpoint(ExceptionService exceptionService) {
            this.exceptionService = exceptionService;
        }
        
        public java.util.Map<String, Object> getStatistics() {
            try {
                return exceptionService.getExceptionStatistics();
            } catch (Exception e) {
                log.error("获取异常统计失败", e);
                return java.util.Map.of("error", e.getMessage());
            }
        }
        
        public java.util.Map<String, Object> getTrends(int days) {
            try {
                return exceptionService.getExceptionTrends(days);
            } catch (Exception e) {
                log.error("获取异常趋势失败", e);
                return java.util.Map.of("error", e.getMessage());
            }
        }
        
        public java.util.Map<String, Object> updateStatus(String exceptionId, String status, String remark) {
            java.util.Map<String, Object> result = new java.util.HashMap<>();
            
            try {
                boolean success = exceptionService.updateExceptionStatus(exceptionId, status, remark);
                result.put("success", success);
                result.put("message", success ? "状态更新成功" : "状态更新失败");
                result.put("timestamp", System.currentTimeMillis());
                
                log.info("通过端点更新异常状态: {} -> {}", exceptionId, status);
                
            } catch (Exception e) {
                log.error("更新异常状态失败: {}", exceptionId, e);
                result.put("success", false);
                result.put("message", "更新失败: " + e.getMessage());
                result.put("timestamp", System.currentTimeMillis());
            }
            
            return result;
        }
        
        public java.util.Map<String, Object> getHealthStatus() {
            try {
                return exceptionService.getHealthStatus();
            } catch (Exception e) {
                log.error("获取健康状态失败", e);
                return java.util.Map.of("error", e.getMessage());
            }
        }
    }
}
